package com.shareware.common.secure.aes

import com.shareware.common.DEFAULT
import com.shareware.common.utils.hexToByteArray
import com.shareware.common.utils.log
import com.shareware.common.utils.toHexString
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec

/**
 * desc: 功能描述
 * email: mobiledeveloper@qq.com
 */
class AesCbc private constructor() {

    companion object {
        private const val algorithmName = "AES"
        private const val transformationCbc = "AES/CBC/PKCS5Padding"

        //AES密钥长度为128bit、192bit、256bit，默认为128bit，一个字节8位
        private const val keyLength = 16

        @JvmStatic
        fun encrypt(content: String, key: String): String {
            return encrypt(content, key.hexToByteArray())
        }

        fun encrypt(content: String, keyBytes: ByteArray): String {
            if (content.isNotEmpty() && keyBytes.size == keyLength) {
                val ivBytes = generateSecureRandom()
                val result = encrypt(content.toByteArray(), keyBytes, ivBytes)
                return mixContent(ivBytes.toHexString(), result.toHexString())
            }
            log(DEFAULT, "cbc encrypt param is not right, key size must be $keyLength")
            return ""
        }

        fun encrypt(contentBytes: ByteArray, keyBytes: ByteArray, ivBytes: ByteArray): ByteArray {
            if (contentBytes.isNotEmpty() && keyBytes.size == keyLength && ivBytes.size == keyLength) {
                try {
                    val secretKeySpec = SecretKeySpec(keyBytes, algorithmName)
                    val cipher = Cipher.getInstance(transformationCbc)
                    val ivParameterSpec = IvParameterSpec(ivBytes)
                    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
                    return cipher.doFinal(contentBytes)
                } catch (ignore: Exception) {
                    log(DEFAULT, "${ignore.message}")
                }
            }
            log(DEFAULT, "cbc encrypt param is not right, key and random size must be $keyLength")
            return byteArrayOf()
        }

        @JvmStatic
        fun decrypt(cipherText: String, key: String): String {
            return decrypt(cipherText, key.hexToByteArray())
        }

        fun decrypt(cipherText: String, keyBytes: ByteArray): String {
            if (cipherText.isNotEmpty() && keyBytes.size == keyLength) {
                val ivString = getIvOrSource(cipherText, true)
                val result = getIvOrSource(cipherText, false)
                return decrypt(result.hexToByteArray(), keyBytes, ivString.hexToByteArray())
            }
            log(DEFAULT, "iv or encrypted word is not right")
            return ""
        }

        fun decrypt(cipherBytes: ByteArray, keyBytes: ByteArray, ivBytes: ByteArray): String {
            if (cipherBytes.isNotEmpty() && keyBytes.size == keyLength && ivBytes.size == keyLength) {
                val secretKeySpec = SecretKeySpec(keyBytes, algorithmName)
                try {
                    val cipher = Cipher.getInstance(transformationCbc)
                    val ivParameterSpec = IvParameterSpec(ivBytes)
                    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
                    return cipher.doFinal(cipherBytes).toString(Charsets.UTF_8)
                } catch (ignore: Exception) {
                    log(DEFAULT, "${ignore.message}")
                }
            }
            log(DEFAULT, "cbc decrypt param is not right")
            return ""
        }

        private fun getIvOrSource(cipherText: String, iv: Boolean): String {
            return if (cipherText.isEmpty()) {
                ""
            } else {
                try {
                    val builder = StringBuilder()
                    if (iv) {
                        builder.append(cipherText.substring(6, 12))
                        builder.append(cipherText.substring(16, 26))
                        builder.append(cipherText.substring(32, 48))
                    } else {
                        builder.append(cipherText.substring(0, 6))
                        builder.append(cipherText.substring(12, 16))
                        builder.append(cipherText.substring(26, 32))
                        builder.append(cipherText.substring(48))
                    }
                    builder.toString()
                } catch (ignore: Exception) {
                    ""
                }
            }
        }

        /**
         * 使用随机数产生的初始化向量才能达到语义安全
         */
        private fun generateSecureRandom(): ByteArray {
            return try {
                val random = SecureRandom()
                val numBytes = ByteArray(keyLength)
                random.nextBytes(numBytes)
                numBytes
            } catch (ignore: Exception) {
                byteArrayOf()
            }
        }

        private fun mixContent(secure: String, cipherText: String): String {
            return if (secure.length > keyLength && cipherText.length > keyLength) {
                try {
                    val strBuilder = StringBuilder()
                    strBuilder.append(cipherText.substring(0, 6))
                    strBuilder.append(secure.substring(0, 6))
                    strBuilder.append(cipherText.substring(6, 10))
                    strBuilder.append(secure.substring(6, 16))
                    strBuilder.append(cipherText.substring(10, 16))
                    strBuilder.append(secure.substring(16))
                    strBuilder.append(cipherText.substring(16))
                    strBuilder.toString()
                } catch (ignore: Exception) {
                    ""
                }
            } else {
                ""
            }
        }
    }
}